#include "iomanger.h"
#include "log.h"

#include <fcntl.h>
#include <string.h>
#include <bitset>

namespace learnco {

static learnco::Logger::ptr g_logger = learnco::LoggerMgr::GetInstance()->getLogger("scheduler");



IOmanger::IOmanger(size_t threads, const std::string& name , bool debug)
    :Scheduler(threads, name, debug)
    ,m_debug(debug){

        m_epollfd = epoll_create1(0);
        assert(m_epollfd>0);

        assert(!(pipe(m_pipefd)));
        fcntl(m_pipefd[0],F_SETFL,O_NONBLOCK);

        epoll_event ev;
        ev.events =  EPOLLIN | EPOLLET;
        ev.data.fd = m_pipefd[0];
        assert(!epoll_ctl(m_epollfd, EPOLL_CTL_ADD, m_pipefd[0], &ev));

        contextResize(32);
        
        this->Scheduler::start();
}
IOmanger::~IOmanger(){
    GetThis()->stop();
}

bool IOmanger::addEvent(int fd, Event event, std::function<void()> cb)
{
    FdContext *fd_ctx = nullptr;
    RWMutex::readRaiiImpl LOCK(m_mutex);
    if(static_cast<int>(m_fdContexts.size()) > fd){
        fd_ctx = static_cast<FdContext*>(m_fdContexts[fd]);
        LOCK.unlock();
    }
    else{
        LOCK.unlock();
        RWMutex::writeRaiiImpl LOCKW(m_mutex);
        contextResize(fd*1.5);
        fd_ctx = static_cast<FdContext*>(m_fdContexts[fd]);
    }

    FdContext::MutexType::RaiiImpl LOCKFD(fd_ctx->fd_mutex);
    //该fd中，确保还未添加过event类型事件，否则已经有读事件等待被触发了，又来一个读事件？
    if((fd_ctx->events & event)){
        LEARNCO_LOG_WARN(g_logger) << "fd= " << fd << ", event= " << event << " 已经存在，将会覆盖之前";
    }

    int op = fd_ctx->events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
    epoll_event ev;    
    ev.events = EPOLLET | fd_ctx->events | event;
    //typedef union epoll_data,  联合，data.fd 和 data.ptr只能使用一个，否则会覆盖
    //ev.data.fd = fd; 必须注释
    ev.data.ptr = fd_ctx;
    assert(!epoll_ctl(m_epollfd, op, fd, &ev));

    ++m_pendingEventnum;
    fd_ctx->events = (Event) (fd_ctx->events | event);
    FdContext::Eventctx& event_ctx = fd_ctx->getEventctx(event);
    //该fd没有还未执行的event事件
    //assert(!event_ctx.cb && !event_ctx.fiber);
    //如果存在未执行的event事件，直接覆盖~


    if(cb){
        event_ctx.cb = cb;
    }else
    {
        event_ctx.fiber = Fiber::GetThis() ;
    }

    //cb 或者fiber 必须存在一个
    assert(event_ctx.cb || event_ctx.fiber);
    return true;
}



bool IOmanger::delEvent(int fd, Event event, bool istrig){
    //确保fd被注册过
    RWMutex::readRaiiImpl LOCK(m_mutex);
    if(fd >= static_cast<int>(m_fdContexts.size())){
        return false;
    }
    FdContext *fd_ctx = static_cast<FdContext*>(m_fdContexts[fd]);
    LOCK.unlock();
    
    FdContext::MutexType::RaiiImpl LOCKFD(fd_ctx->fd_mutex);
    //fd中不存在该事件
    if(!(fd_ctx->events & event))
    {
        LEARNCO_LOG_WARN(g_logger) << "fd=" << fd << " 没有该事件，无法删除";
        return false;
    }

    Event new_events = (Event)(fd_ctx->events & ~event);
    int op = new_events? EPOLL_CTL_MOD : EPOLL_CTL_DEL;

    epoll_event ev;
    ev.events = EPOLLET | new_events;
    ev.data.ptr = fd_ctx;

    assert(!epoll_ctl(m_epollfd, op, fd, &ev));

    //触发，放入scheduler任务队列执行
    if(istrig)
    {
        fd_ctx->triggerEvent(event);
        if(Scheduler::m_idleThreadCount){
            v_ticked();
        }
    }

    fd_ctx->events = new_events;
    FdContext::Eventctx& event_ctx = fd_ctx->getEventctx(event);
    event_ctx.cb = nullptr;
    event_ctx.fiber.reset();
    --m_pendingEventnum;
    return true;
}

bool IOmanger::delEventAll(int fd, bool istrig){
    //确保fd被注册过
    RWMutex::readRaiiImpl LOCK(m_mutex);
    if(fd >= static_cast<int>(m_fdContexts.size())){
        return false;
    }
    FdContext *fd_ctx = static_cast<FdContext*>(m_fdContexts[fd]);
    LOCK.unlock();

    FdContext::MutexType::RaiiImpl LOCKFD(fd_ctx->fd_mutex);
    if(fd_ctx->events == NONE){
        return false;
    }

    int op = EPOLL_CTL_DEL;
    epoll_event ev;
    ev.events = 0;
    ev.data.ptr = fd_ctx;
    assert(!epoll_ctl(m_epollfd, op, fd, &ev));

    
    if(fd_ctx->events & READ)
    {
        
        if(istrig){
            fd_ctx->triggerEvent(READ);
        } 
        if(Scheduler::m_idleThreadCount){
            v_ticked();
        }

        fd_ctx->events = (Event)(fd_ctx->events & (~READ));
        --m_pendingEventnum;
    }
    if(fd_ctx->events & WRITE)
    {
        if(istrig) {
            fd_ctx->triggerEvent(WRITE);
        }

        if(Scheduler::m_idleThreadCount){
            v_ticked();
        }

        fd_ctx->events = (Event)(fd_ctx->events & (~WRITE));
        --m_pendingEventnum;
    }

    //此时fd_ctx应无任何事件
    assert(!fd_ctx->events);
    return true;
}

void IOmanger::contextResize(size_t size)
{
    m_fdContexts.resize(size);

    for(size_t i=0; i < size; ++i)
    {
        if(!m_fdContexts[i]){
            m_fdContexts[i] = static_cast<FdContext*>(new FdContext);
            m_fdContexts[i]->fd = i;
        }
    }
}

IOmanger::FdContext::Eventctx& IOmanger::FdContext::getEventctx(Event event){
    switch (event)
    {
    case READ: return read;
    case WRITE: return write;
    default: assert(1); return read;
    }
}

void IOmanger::FdContext::triggerEvent(Event event){

    Eventctx& ctx = getEventctx(event);
    Scheduler* sched = Scheduler::GetThis();
    assert(ctx.cb || ctx.fiber);
    ctx.cb ? sched->scheduler(ctx.cb):sched->scheduler(ctx.fiber);
}


//override Scheduler
void IOmanger::v_idle(){    

    const int MAX_EVENTS = 128;
    const int MAX_TIMEOUT = 5000;
    epoll_event* events = new epoll_event[MAX_EVENTS];
    std::shared_ptr<epoll_event> shared_events(events, [](epoll_event *ptr){delete[] ptr;});

    
    while(true){
        if(m_debug) LEARNCO_LOG_INFO(g_logger) << "enter iomanger idle" ;
        //获取离最近定时器间隔时间
        int nexttime;
        nexttime = getNextTime();
        nexttime = nexttime > MAX_TIMEOUT ? MAX_TIMEOUT : nexttime; //限制最大等待时间

        //陷入epoll_wait等待事件
        int nr;
        do {
            nr = epoll_wait(m_epollfd, events, MAX_EVENTS, nexttime);
        } while (nr < 0 && errno == EINTR);

        std::vector<std::function<void()>> cbs;
        listExpiredCb(cbs);
        for(auto cb : cbs){
            GetThis()->scheduler(cb);
        }
        
        for(int i=0; i<nr; ++i){
            epoll_event& event = events[i];

            if(event.data.fd == m_pipefd[0]){
                uint8_t dummy[4];
                while(read(m_pipefd[0], dummy, sizeof(dummy)) > 0);
                if(m_debug) LEARNCO_LOG_INFO(g_logger) << "epoll_wait ticked... " << nr;
                continue;
            }

            FdContext* fd_ctx = static_cast<FdContext*>(event.data.ptr);

            FdContext::MutexType::RaiiImpl LOCK(fd_ctx->fd_mutex);

            if (event.events & (EPOLLERR | EPOLLHUP)) {
                /* 监控到错误或者挂起(长时间未响应) 强制触发fd下的所有事件*/
                LEARNCO_LOG_ERROR(g_logger) << "fd:  " << fd_ctx->fd << "  EPOLLERR| EPOLLHUP";
                event.events = fd_ctx->events;
            }

            int real_event = NONE;
            if(event.events & EPOLLIN){
                real_event |= READ;
            }
            
            if(event.events & EPOLLOUT){
                real_event |= WRITE;
            }

            //fd中没有注册过该事件
            if((fd_ctx->events & real_event) == NONE){
                continue;
            }

            int left_event = fd_ctx->events & (~real_event);
            fd_ctx->events = (Event)left_event;
            
            int op = left_event ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
            event.events = EPOLLET | left_event;
            assert(!epoll_ctl(m_epollfd, op, fd_ctx->fd, &event));
            

            if(real_event & READ){
                 fd_ctx->triggerEvent(READ);
                 --m_pendingEventnum;
            }

            if(real_event & WRITE){
                 fd_ctx->triggerEvent(WRITE);
                 --m_pendingEventnum;
            }
        }

        //scheduler有任务了，scheduler会跳转到run去取任务
        Fiber::ptr cur = Fiber::GetThis();
        auto raw_ptr = cur.get();
        cur.reset();
        raw_ptr->swapOut();
    }
}

void IOmanger::v_ticked(){
    if(m_debug) LEARNCO_LOG_INFO(g_logger) << "v_ticked" ;
    assert(write(m_pipefd[1], "T", 1));
}

void IOmanger::v_onTimerInsertedAtFront()
{
    v_ticked();
}

size_t IOmanger::getEventnum()
{
    return m_pendingEventnum;
}


IOmanger* IOmanger::GetThis(){
    //基类指针，转换成派生类组指针
    return dynamic_cast<IOmanger*> (Scheduler::GetThis());
}


}